// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-12-05

package endpoint

import (
	"encoding/json"
	"gitee.com/gomq/sr/filter"
	"gitee.com/gomq/sr/name"
	"time"
)

const (
	defaultSubscriptionConcurrency int32 = 10
	defaultSubscriptionMaxRetry    int   = 5
	defaultSubscriptionThreads     int   = 1
)

type (
	Subscription interface {
		GetBroadcast() bool
		GetConcurrency() int32
		GetDelay() time.Duration
		GetDelayMilliseconds() int64
		GetDelaySeconds() int
		GetEndpoint() (Endpoint, bool)
		GetFailedEndpoint() (Endpoint, bool)
		GetFilter() (filter filter.Filter, has bool)
		GetId() int
		GetMaxRetry() int
		GetStore() bool
		GetSucceedEndpoint() (Endpoint, bool)
		GetTag() name.TopicTag
		GetThreads() int
		GetTopic() name.TopicName

		// Setter.

		SetBroadcast(v bool) Subscription
		SetConcurrency(n int32) Subscription
		SetDelaySeconds(n int) Subscription
		SetFilter(filter filter.Filter) Subscription
		SetId(n int) Subscription
		SetMaxRetry(n int) Subscription
		SetStore(store bool) Subscription
		SetTag(tag name.TopicTag) Subscription
		SetThreads(n int) Subscription
		SetTopic(topic name.TopicName) Subscription

		// With endpoints.

		WithEndpoint(endpoint Endpoint)
		WithFailedNotification(endpoint Endpoint)
		WithSucceedNotification(endpoint Endpoint)
	}

	subscription struct {
		Broadcast         bool           `json:"Broadcast"`
		Concurrency       int32          `json:"Concurrency,omitempty"`
		Delay             time.Duration  `json:"-"`
		DelaySeconds      int            `json:"DelaySeconds,omitempty"`
		DelayMilliseconds int64          `json:"-"`
		Filter            filter.Filter  `json:"Filter,omitempty"`
		FilterHas         bool           `json:"-"`
		Id                int            `json:"Id,omitempty"`
		MaxRetry          int            `json:"MaxRetry,omitempty"`
		Store             bool           `json:"Store"`
		Tag               name.TopicTag  `json:"Tag,omitempty"`
		Topic             name.TopicName `json:"Topic,omitempty"`
		Threads           int            `json:"Threads,omitempty"`

		ep, fep, sep Endpoint
	}
)

func NewSubscription() Subscription { return (&subscription{}).init() }

// Getter.

func (o *subscription) GetBroadcast() bool                   { return o.Broadcast }
func (o *subscription) GetConcurrency() int32                { return o.Concurrency }
func (o *subscription) GetDelay() time.Duration              { return o.Delay }
func (o *subscription) GetDelayMilliseconds() int64          { return o.DelayMilliseconds }
func (o *subscription) GetDelaySeconds() int                 { return o.DelaySeconds }
func (o *subscription) GetEndpoint() (Endpoint, bool)        { return o.ep, o.ep != nil }
func (o *subscription) GetFailedEndpoint() (Endpoint, bool)  { return o.fep, o.fep != nil }
func (o *subscription) GetFilter() (filter.Filter, bool)     { return o.Filter, o.FilterHas }
func (o *subscription) GetId() int                           { return o.Id }
func (o *subscription) GetMaxRetry() int                     { return o.MaxRetry }
func (o *subscription) GetStore() bool                       { return o.Store }
func (o *subscription) GetSucceedEndpoint() (Endpoint, bool) { return o.sep, o.sep != nil }
func (o *subscription) GetTag() name.TopicTag                { return o.Tag }
func (o *subscription) GetThreads() int                      { return o.Threads }
func (o *subscription) GetTopic() name.TopicName             { return o.Topic }

// + Setter.

func (o *subscription) SetBroadcast(v bool) Subscription {
	o.Broadcast = v
	return o
}

func (o *subscription) SetConcurrency(n int32) Subscription {
	if n > 0 {
		o.Concurrency = n
	}
	return o
}

func (o *subscription) SetDelaySeconds(n int) Subscription {
	if n >= 0 {
		o.Delay = time.Duration(n) * time.Second
		o.DelayMilliseconds = int64(n) * 1000
		o.DelaySeconds = n
	}
	return o
}

func (o *subscription) SetFilter(filter filter.Filter) Subscription {
	o.Filter = filter
	o.FilterHas = filter != nil
	return o
}

func (o *subscription) SetId(n int) Subscription {
	if n > 0 {
		o.Id = n
	}
	return o
}

func (o *subscription) SetMaxRetry(n int) Subscription {
	if n > 0 {
		o.MaxRetry = n
	}
	return o
}

func (o *subscription) SetStore(store bool) Subscription {
	o.Store = store
	return o
}

func (o *subscription) SetTag(tag name.TopicTag) Subscription {
	if !tag.Undefined() {
		o.Tag = tag
	}
	return o
}

func (o *subscription) SetThreads(n int) Subscription {
	if n > 0 {
		o.Threads = n
	}
	return o
}

func (o *subscription) SetTopic(topic name.TopicName) Subscription {
	if !topic.Undefined() {
		o.Topic = topic
	}
	return o
}

func (o *subscription) WithEndpoint(endpoint Endpoint) {
	o.ep = endpoint
}

func (o *subscription) WithFailedNotification(endpoint Endpoint) {
	o.fep = endpoint
}

func (o *subscription) WithSucceedNotification(endpoint Endpoint) {
	o.sep = endpoint
}

// + Common

func (o *subscription) String() string {
	if buf, err := json.Marshal(o); err == nil {
		return string(buf)
	}
	return ""
}

// + Access methods

func (o *subscription) init() *subscription {
	o.Concurrency = defaultSubscriptionConcurrency
	o.MaxRetry = defaultSubscriptionMaxRetry
	o.Threads = defaultSubscriptionThreads
	return o
}
